package com.qf.security1.conf;

import com.alibaba.fastjson.JSON;
import com.qf.security1.filters.LoginFilter;
import com.qf.security1.filters.VerifyTokenFilter;
import com.qf.security1.mapper.ResourcesMapper;
import com.qf.security1.pojo.Code;
import com.qf.security1.pojo.R;
import com.qf.security1.pojo.Resources;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter implements InitializingBean {

    @Autowired
    private ResourcesMapper resourcesMapper;

    private List<Resources> resourcesList;

    //在springbean对象创建生命周期的初始化阶段自动执行
    @Override
    public void afterPropertiesSet() throws Exception {
        resourcesList = resourcesMapper.findAll();
    }


    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        //设置跨域
        http.cors().configurationSource(ccs());

        for (Resources resources : resourcesList) {
            List<String> roles = resources.getRoles();
            http.authorizeRequests().antMatchers(resources.getUrl()).hasAnyRole(roles.toArray(new String[0]));
        }

        //配置哪些资源的访问需要登录 （***注意：在指定路径需要认证或者放行时，一定要将大的路径范围放最后）
        http.authorizeRequests()
                //表示所有资源的访问必须登录
                .antMatchers("/**").authenticated();

        http.exceptionHandling()
                //未登录时的响应数据
                .authenticationEntryPoint(new AuthenticationEntryPoint() {
                    @Override
                    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                        //当访问资源需要登录，但是没有登录时，会执行这个回调
                        R r = R.fail(Code.NOLOGIN, "尚未登录");

                        response.setContentType("application/json;charset=utf-8");
                        response.getWriter().write(JSON.toJSONString(r));
                    }
                })
                //当访问的资源需要权限但是没有权限时，会触发的回调
                .accessDeniedHandler(new AccessDeniedHandler() {
                    @Override
                    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                        R r = R.fail(Code.NOAUTH, "没有访问权限");

                        response.setContentType("application/json;charset=utf-8");
                        response.getWriter().write(JSON.toJSONString(r));
                    }
                });


        //将登录过滤器添加到security过滤器链中
        http.addFilter(new LoginFilter(authenticationManager()))
                .addFilter(new VerifyTokenFilter(authenticationManager()));

    }

    /**
     * 跨域访问设置
     * @return
     */
    private CorsConfigurationSource ccs() {
        UrlBasedCorsConfigurationSource ubccs = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfig = new CorsConfiguration();
        //允许哪些域的访问, * 表示任何域都可以访问
        corsConfig.setAllowedOrigins(Arrays.asList("*"));
        //允许哪些请求方式的访问
        corsConfig.setAllowedMethods(Arrays.asList("*"));
        //跨域访问时，允许携带的请求头有哪些 (排除cookie的)
        corsConfig.setAllowedHeaders(Arrays.asList("*"));
        //跨域访问时，允许携带的响应头有哪些 (排除cookie的)
        corsConfig.setExposedHeaders(Arrays.asList("*"));

        //任何资源的访问，都按照我们指定的corsConfig配置来进行跨域限定
        ubccs.registerCorsConfiguration("/**",corsConfig);
        return ubccs;
    }

}
